package com.ai.aspect;

import com.ai.common.ParentResult;
import com.ai.common.cache.TimeExpiredPoolCache;
import com.ai.common.config.RateLimiter;
import com.ai.common.enums.ConfigModuleEnum;
import com.ai.common.gpt.subscriber.OpenAIMessageResponse;
import com.ai.common.utils.ConfigUtil;
import com.ai.common.utils.TraceContext;
import com.ai.dao.entity.ai.AiUser;
import com.ai.repository.ai.AiUserRepository;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.CodeSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import reactor.core.publisher.Flux;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * 全局接口切面
 */
@Slf4j
@Order(-1000)
@Aspect
@Component
public class GlobalAspect {

    @Autowired
    private AiUserRepository aiUserRepository;

    @Pointcut("execution(public * com.ai.web.*Controller.*(..)) && !within(com.ai.web.AbcController) && !within(com.ai.web.PublicController) && !within(com.ai.web.LoginController)")
    public void webAspect() {}
    @Around("webAspect()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Type returnType = null;
        int code = 1;
        String msg = "网络异常，请稍后重试！";
        //请求路径
        String requestURI = null;
        try {
//            if (true){
//                return getResultInstance(returnType,code,"网络异常，请稍后重试！");
//            }

            //方法名称
            String methodName = joinPoint.getSignature().getName();
            //获取方法返回值类型
            Object[] args = joinPoint.getArgs();
            Class<?>[] paramsCls = new Class<?>[args.length];
            for (int i = 0; i < args.length; ++i) {
                paramsCls[i] = args[i].getClass();
            }
            //获取方法
            Method method = joinPoint.getTarget().getClass().getMethod(joinPoint.getSignature().getName(), paramsCls);
            //获取返回值类型
            returnType = method.getAnnotatedReturnType().getType();

            String uid = null;
            //获取请求头和请求参数
            RequestAttributes ra = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes sra = (ServletRequestAttributes) ra;
            HttpServletRequest request = null;
            if (sra != null) {
                request = sra.getRequest();
                requestURI = request.getRequestURI();
                // 获取请求头
                Enumeration<String> enumeration = request.getHeaderNames();
                while (enumeration.hasMoreElements()) {
                    String name = enumeration.nextElement();
                    String value = request.getHeader(name);
                    if ("uid".equals(name)){
                        uid = value;
                        break;
                    }
                }
            }
            ////////////////////////////
//            //获取请求参数
//            Map<String, Object> parameterMap = new HashMap<>();
//            parameterMap.put("headUid",uid);
//            String[] names = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
//            for (int i = 0; i < names.length; i++) {
//                parameterMap.put(names[i], args[i]);
//            }
//            //打印请求参数
//            log.info(requestURI + " request " + JSON.toJSONString(parameterMap));
            //////////////////////////////

            //跳过指定接口
            if (methodName.contains("upload")//上传文件接口
                    || methodName.contains("getCurDateUser")//获取当天新用户
                    || methodName.contains("isShowAdvertising")//是否展示广告
                    || methodName.contains("Test")//测试
                    || methodName.contains("test")//测试
                    || methodName.equals("calculateNowVolumeSurge")//计算成交量
                    || methodName.equals("syncNewEnergy")//同步新能源
                    || methodName.equals("syncStockBlockTradeByDate")//同步大宗交易
                    || methodName.equals("chatroomFluxGet")//流式gpt
                    || methodName.equals("syncAll")//同步所有配置
                    || methodName.equals("share")//分享接口
                    || methodName.equals("getUserByWXCode")){//获取用户openId接口
                return joinPoint.proceed();
            }

            //uid为空时返回
            if (StringUtils.isBlank(uid)) {
                log.warn(requestURI + " -> uid不能为空");
                msg = "用户不存在，请重新进入小程序";
                if ("reactor.core.publisher.Flux<java.lang.String>".equals(returnType.getTypeName())){
                    return Flux.just(JSON.toJSONString(new OpenAIMessageResponse(msg, null)), JSON.toJSONString(new OpenAIMessageResponse("", true)));
                }else {
                    return getResultInstance(returnType,code,msg);
                }
            }

            //配置是否限流
            String isLimiter = ConfigUtil.getInstance().getConfig(ConfigModuleEnum.DEFAULT.getModule(), "isLimiter");
            if (isLimiter == null || !isLimiter.equals("true")){
                //获取并保存用户信息
                Object obj = getAndSaveUserInfo(returnType, uid);
                if (obj != null){
                    return obj;
                }
                return joinPoint.proceed();
            }

            //获取用户令牌桶对象
            RateLimiter userRateLimiter = TimeExpiredPoolCache.getInstance().get(uid);
            if (userRateLimiter == null){
                //最大令牌数为4，每秒补充3个令牌的限流器
                userRateLimiter = new RateLimiter(4, 3);
                //保存用户令牌桶对象缓存 设置失效时间1分钟
                TimeExpiredPoolCache.getInstance().put(uid,userRateLimiter,60*1000L);
            }
            //使用tryAcquire()方法进行限流,如果令牌桶中有令牌，则获取成功，执行接口调用；否则获取失败，返回接口访问频率过高的错误信息。在获取令牌时，限流器会自动补充令牌，确保令牌桶中的令牌数量不超过最大令牌数。
            if (userRateLimiter.tryAcquire()) {
                //执行接口调用
                //获取并保存用户信息
                Object obj = getAndSaveUserInfo(returnType, uid);
                if (obj != null){
                    return obj;
                }
                return joinPoint.proceed();
            } else {
                // 返回接口访问频率过高的错误信息
                log.error("【限流触发】 -> " + uid + " 访问频率过高");
                return getResultInstance(returnType,code,"访问频率过高,请稍后再试");
            }
        }catch (Exception e){
            log.error(e.getMessage(),e);
            return getResultInstance(returnType,code,msg);
        }finally {
            TraceContext.clearCurrent();
        }
    }

    /**
     * 获取并保存用户信息
     * @param returnType
     * @param uid
     * @return
     * @throws Exception
     */
    private Object getAndSaveUserInfo(Type returnType,String uid) throws Exception {
        //获取用户信息
        AiUser aiUser = aiUserRepository.getUserByUid(uid);
        if (aiUser == null){
            log.warn("GlobalAspect检测用户不存在，uid=" + uid);
            int code = 2;
            String msg = "用户不存在,请正确使用！";
            if ("reactor.core.publisher.Flux<java.lang.String>".equals(returnType.getTypeName())){
                return Flux.just(JSON.toJSONString(new OpenAIMessageResponse(msg, null)), JSON.toJSONString(new OpenAIMessageResponse("", true)));
            }else {
                return getResultInstance(returnType,code,msg);
            }
        }
        //保存用户信息
        TraceContext.getCurrent().addTags("aiUser", JSON.toJSONString(aiUser));
        return null;
    }

    /**
     * 通过反射实例化返回参数并设置code和msg属性
     * @param returnType
     * @param code
     * @param msg
     * @return
     * @throws Exception
     */
    private Object getResultInstance(Type returnType, int code, String msg) throws Exception {
        if (returnType == null){
            ParentResult parentResult = new ParentResult();
            parentResult.setCode(code);
            parentResult.setMsg(msg);
            return parentResult;
        }
        //通过反射实例化返回对象
        Object newInstance = Class.forName(returnType.getTypeName()).newInstance();
        //获取返回对象的setMsg()方法
        Method msgMethod = newInstance.getClass().getMethod("setMsg", String.class);
        Method codeMethod = newInstance.getClass().getMethod("setCode", Integer.class);
        //设置返回值
        msgMethod.invoke(newInstance,msg);
        codeMethod.invoke(newInstance,code);
        return newInstance;
    }
}
